// server.js
const express = require('express');
const https = require('https');
const fs = require('fs');
const sock = require('socket.io');
const { v4: uuidv4 } = require('uuid');
const mediasoup = require("mediasoup");
const Room = require("./Room.js");
const Peer = require("./Peer.js");

// 读取 SSL 证书
const server = https.createServer({
  cert: fs.readFileSync('../cert.pem'),
  key: fs.readFileSync('../key.pem')
}, express());

let workers = new Array();
// worker代表一个mediasoup c++子进程，它运行在单个CPU内核中，并处理Router实例
// 这里暂时写死为1 ， 因为我服务器只有单核。
let numWorkers = 1;

// 异步创建所有的worker
(async function() {
  console.log("初始化：创建worker");
  for (let index = 0; index < numWorkers; index++) {
    // 创建 Worker (媒体核心进程)
    let worker = await mediasoup.createWorker(
      {
        logLevel            : "warn",
        dtlsCertificateFile : "../cert.pem",
        dtlsPrivateKeyFile  : "../key.pem",
        appData             : { foo: 123 }  // 怎么写都行，自定义数据
      });
      workers.push(worker);
      console.log("创建worker成功，此时workers有："+workers.length);
  
  
      // 新worker建立会输出日志
      mediasoup.observer.on("newworker", (worker) =>
      {
        console.log("new worker created [pid:%d]", worker.pid);
      });
  
      // worker 异常死亡
      worker.on("died", (error) =>
      {
        console.error("mediasoup worker died!: %o", error);
      });
  }
})();


  // 查看mediasoup支持的RTP功能
const rtpCapabilities = mediasoup.getSupportedRtpCapabilities();
console.log(`server rtpCapabilities is : ${JSON.stringify(rtpCapabilities)} `);


// socket.io 中文文档：https://socket.io/zh-CN/docs/v4/emitting-events/
// 创建 WebSocket 服务器
const io = sock(server,{
  cors: {
    origin: '*' // 配置跨域
  }
});
console.log("初始化：创建 WebSocket 服务器");

// 所有房间列表
let roomList = new Map();
// 房间密码列表
let roomPassword = new Map();

// 监听 WebSocket 连接
io.on('connection', socket => {

  console.log("WebSocket有新连接接入！");


  // 测试callback好不好用
  socket.on('createRoom', async ({room_id, password},callback) => {
    
    if (roomList.has(room_id)) {
      console.error(`已经有 ${room_id} 房间了！`)
      callback(`已经有 ${room_id} 房间了！`);
    } else{
      console.log(`创建房间，房间id为：${room_id}，密码是：${password}`);
      let worker = await getWorker();

      // 创建房间
      // let room = new Room(room_id, worker, io);
      // 在这里调用静态工厂方法创建房间
      roomList.set(room_id, await Room.create(room_id, worker, io));
      // console.log(`已经创建好房间，此时room对象内部的roomid为：${room.room_id}`);
      console.log(`已经创建好房间，此时roomList对象为：${roomList}`);
      console.log(`已经创建好房间，准备回调`);
      console.log(`执行回调`);
      callback(room_id);
      console.log(`发送事件`);
      socket.emit("createRoom callback", room_id);
    }
  });


  socket.on('join',async ({room_id, name, password},callback) => {
    
    console.log('---user joined--- "' + room_id + '": ' + name);
    console.log(`当前roomList：${JSON.stringify(roomList)}`);
    if (!roomList.has(room_id)) {
      console.error("房间不存在！！");
    } else {
      // TODO 房间密码校验
      
      // 获取进入房间之前的人
      let room = roomList.get(room_id);
      console.log(`准备往当前房间里添加一个新的人：${name}`);
      // 往房间里添加一个人
      room.addPeer(new Peer(socket.id, name));
      socket.room_id = room_id;

      // 广播
      // room.broadCast(socket.id, "joined", {
      //   room_id: room_id,
      //   name: name,
      //   socketid: socket.id,
      // });
      // 广播到房间内所有人有新人加入了
      console.log(`即将广播所有人，新人加入了`);
      room.broadCast(socket.id, 'newMembers', [
        {
          socketid: socket.id,
          name: name,
        }
      ])

      // 返回服务端此时的room
      callback(roomList.get(room_id).toJson())
      
      // setTimeout(() => {
      //   socket.emit("othersInRoom", peerInfos);
      // }, 1);
      
    }
  });

  socket.on('getProducers', () => {
    if (!roomList.has(socket.room_id)) return
    console.log('Get producers', { name: `${roomList.get(socket.room_id).getPeers().get(socket.id).name}` })

    // send all the current producer to newly joined member
    let producerList = roomList.get(socket.room_id).getProducerListForPeer()

    console.log(`当前服务器内有producer的个数：${producerList.length}`);
    // 这个producerList，就是房间内所有，注意是所有的视频发送者的id
    socket.emit('newProducers', producerList)
  })


  socket.on('getAllRoomMembers', ({room_id},callback) => {
    if (!roomList.has(socket.room_id)) return
    console.log(`进入 getAllRoomMembers,room_id: ${room_id}`)
    // 房间内当前的所有人
    let room = roomList.get(room_id);
    let peers = room.getPeers();
    
    let peerInfos = new Array();
    if (peers) {
      for (let peerid of Array.from(peers.keys())) {
        peerInfos.push({
          socketid: peerid,
          name: peers.get(peerid).name,
        });
      }
    }
    console.log(`当前房间里面的人有：${JSON.stringify(peerInfos)}`);

    callback(peerInfos)
  })

  socket.on('getRouterRtpCapabilities', (callback) => {
    console.log('Get RouterRtpCapabilities', {
      name: `${roomList.get(socket.room_id).getPeers().get(socket.id).name}`
    })
    let room = roomList.get(socket.room_id);
    let roomRtpCapabilities = room.getRtpCapabilities()
    callback(roomRtpCapabilities)
    
  })

  socket.on("createWebRtcTransport",async (parameter1,callback) => {
      console.log(`进入 createWebRtcTransport，接收从客户端传来的参数1：${JSON.stringify(parameter1)}`);
      // WebRtcTransport 代表一个客户端与 MediaSoup 服务器之间的媒体连接通道。
      console.log('Create webrtc transport', {
        name: `${roomList.get(socket.room_id).getPeers().get(socket.id).name}`
      })
      
      const params = await roomList.get(socket.room_id).createWebRtcTransport(socket.id)
      console.log(`createWebRtcTransport 创建完毕，准备返回结果：${params}`);
      callback(params)
      // TODO 例行发送一个空事件。先占着位再说
      socket.emit("afterCreateWebRtcTransport", "daji");
      
    }
  );


  /**
   * 监听消息  连接传输通道
   */
  socket.on("connectTransport", async ({transport_id, dtlsParameters}, callback) => {
      try {
        console.log(`---connect transport--- room_id: ${socket.room_id}`);
        console.log(`---connect transport--- socketId: ${socket.id}`);
        console.log(`---connect transport--- transport_id: ${transport_id}`);
        
        console.log(
          `---connect transport--- dtlsParameters: ${dtlsParameters}`
        );
        if (!roomList.has(socket.room_id)) return;

        let room = roomList.get(socket.room_id);
        // 服务端获取客户端传回来的  dtlsParameters ，和客户端已经有的一个 Transport进行连接。
        // 其实就是客户端的Transport和服务端的Transport建立连接的过程。
        await room.connectPeerTransport(socket.id, transport_id, dtlsParameters);
        console.log(`---connect transport--- connectPeerTransport执行完毕`);

        // 这里必须提供回调函数，如果不提供回调函数必报错无疑。
        callback('success')
      } catch (error) {
        console.error(error);
      }
    }
  );

  /**
   * 监听消息  开始生产
   */
  socket.on("produce", async ({kind, rtpParameters, producerTransportId,name}, callback) => {

      console.log(`---produce --- kind = ${kind}`);
      console.log(`---produce --- name = ${name}`);
      console.log(
        `---produce --- rtpParameters = ${JSON.stringify(rtpParameters)}`
      );
      console.log(
        `---produce --- producerTransportId = ${producerTransportId}`
      );

      if (!roomList.has(socket.room_id)) {
        console.error(`房间号无效，找不到对应的房间！${socket.room_id}`);
        return;
      }

      let room = roomList.get(socket.room_id);
      let producer_id = await room.produce(socket.id, producerTransportId, rtpParameters, kind, name);

      console.log(`---produce 信令服务器生产完毕 --- producer_id = ${producer_id}`);

      callback({
        producer_id
      })
    }
  );
  /**
   * 监听消息 开始消费
   */
  socket.on("consume",async ({consumerTransportId, producerId, rtpCapabilities}, callback) => {
    
      console.log(`---consume consumerTransportId = ${consumerTransportId}`);
      console.log(`---consume producerId = ${producerId}`);
      console.log(`---consume rtpCapabilities = ${rtpCapabilities}`);

      if (typeof rtpCapabilities == "string") {
        console.log(`---consume rtpCapabilities 是string类型，将其解析为JSON对象。`);
        rtpCapabilities = JSON.parse(rtpCapabilities);
      }

      try {
        let room = roomList.get(socket.room_id);
        let params = await room.consume(socket.id,consumerTransportId,producerId,rtpCapabilities)

        console.log(`---consumer is = ${params}`);

        callback(params)

      } catch (error) {
        console.error(error);
      }
    }
  );

  socket.on('resume', async (data, callback) => {
    await consumer.resume()
    callback()
  })

  socket.on('getMyRoomInfo', (_, cb) => {
    cb(roomList.get(socket.room_id).toJson())
  })

  socket.on('disconnect', () => {
    console.log('Disconnect', {
      name: `${roomList.get(socket.room_id) && roomList.get(socket.room_id).getPeers().get(socket.id).name}`
    })

    if (!socket.room_id) return
    roomList.get(socket.room_id).removePeer(socket.id)
  })

  socket.on('producerClosed', ({ producer_id }) => {
    console.log('Producer close', {
      name: `${roomList.get(socket.room_id) && roomList.get(socket.room_id).getPeers().get(socket.id).name}`
    })

    roomList.get(socket.room_id).closeProducer(socket.id, producer_id)
  })

  socket.on('exitRoom', async (_, callback) => {
    console.log('Exit room', {
      name: `${roomList.get(socket.room_id) && roomList.get(socket.room_id).getPeers().get(socket.id).name}`
    })

    if (!roomList.has(socket.room_id)) {
      callback({
        error: 'not currently in a room'
      })
      return
    }
    // close transports
    await roomList.get(socket.room_id).removePeer(socket.id)
    if (roomList.get(socket.room_id).getPeers().size === 0) {
      roomList.delete(socket.room_id)
    }

    socket.room_id = null

    callback('successfully exited room')
  })
  
});

  // 客户端断开时移除
//   socket.on('close', () => {
//     clients = clients.filter(client => client !== socket);
//   });


/**
 * 获取可用worker
 */
 function getWorker() {
  // 我们只有一个Worker。。
  const worker = workers[0];
  return worker;
}


// 启动服务器
server.listen(3000, () => {
  console.log('信令服务器正在运行在 https://localhost:3000');
});
